// netcoad driver
#include "bootpack.h"
#include <string.h>

/*
class E1000 : public NetworkDriver
{
    private:
 
        uint8_t bar_type;     // Type of BAR0
        uint16_t io_base;     // IO Base Address
        uint64_t  mem_base;   // MMIO Base Address
        bool eerprom_exists;  // A flag indicating if eeprom exists
        uint8_t mac [6];      // A buffer for storing the mack address
        struct e1000_rx_desc *rx_descs[E1000_NUM_RX_DESC]; // Receive Descriptor Buffers
        struct e1000_tx_desc *tx_descs[E1000_NUM_TX_DESC]; // Transmit Descriptor Buffers
        uint16_t rx_cur;      // Current Receive Descriptor Buffer
        uint16_t tx_cur;      // Current Transmit Descriptor Buffer
 
 
        // Send Commands and read results From NICs either using MMIO or IO Ports
        void writeCommand( uint16_t p_address, uint32_t p_value);
        uint32_t readCommand(uint16_t p_address);
 
 
        bool detectEEProm(); // Return true if EEProm exist, else it returns false and set the eerprom_existsdata member
        uint32_t eepromRead( uint8_t addr); // Read 4 bytes from a specific EEProm Address
        bool readMACAddress();       // Read MAC Address
        void startLink ();           // Start up the network
        void rxinit();               // Initialize receive descriptors an buffers
        void txinit();               // Initialize transmit descriptors an buffers
        void enableInterrupt();      // Enable Interrupts
        void handleReceive();        // Handle a packet reception.
    public:
 
        E1000(PCIConfigHeader * _pciConfigHeader); // Constructor. takes as a parameter a pointer to an object that encapsulate all he PCI configuration data of the device
        void start ();                             // perform initialization tasks and starts the driver
        void fire (InterruptContext * p_interruptContext);  // This method should be called by the interrupt handler 
        uint8_t * getMacAddress ();                         // Returns the MAC address
        int sendPacket(const void * p_data, uint16_t p_len);  // Send a packet
        ~E1000();                                             // Default Destructor
};
void E1000::writeCommand( uint16_t p_address, uint32_t p_value)
{
    if ( bar_type == 0 )
    {
        MMIOUtils::write32(mem_base+p_address,p_value);
    }
    else
    {
        Ports::outportl(io_base, p_address);
        Ports::outportl(io_base + 4, p_value);
    }
}
uint32_t E1000::readCommand( uint16_t p_address)
{
    if ( bar_type == 0 )
    {
        return MMIOUtils::read32(mem_base+p_address);
    }
    else
    {
        Ports::outportl(io_base, p_address);
        return Ports::inportl(io_base + 4);
    }
}
*/

void writeCommand(unsigned int addr,unsigned int value){
	//io_out32(0xc000,addr);
	//io_out32(0xc000+4,value);
    (*((unsigned int*)(0xfebc0000+addr)))=(value);
	return;
}
int readCommand(unsigned int addr){
	//io_out32(0xc000,addr);
	//return io_in16(0xc000+4);
	return (*((unsigned int*)(0xfebc0000+addr)));
}
int detectEEProm()
{
	int i=0;
    int val = 0;
    writeCommand(REG_EEPROM, 0x1); 
    int eerprom_exists=0;
 
    for(i = 0; i < 1000 && ! eerprom_exists; i++)
    {
            val = readCommand( REG_EEPROM);
            if(val & 0x10)
                    eerprom_exists = 1;
            else
                    eerprom_exists = 0;
    }
    return val;
}
int eepromRead( unsigned int addr)
{
	unsigned int data = 0;
	int tmp = 0;
    writeCommand( REG_EEPROM, (1) | ((unsigned int)(addr) << 8) );
    while( !((tmp = readCommand(REG_EEPROM)) & (1 << 4)) );    
	data = (unsigned int)((tmp >> 16) & 0xFFFF);
	return data;
}
int readMACAddress(struct NetworkDriver *driver)
{
    unsigned int temp;
    temp = eepromRead( 0);
    driver->mac[0] = temp &0xff;
    driver->mac[1] = temp >> 8;
    temp = eepromRead( 1);
    driver->mac[2] = temp &0xff;
    driver->mac[3] = temp >> 8;
    temp = eepromRead( 2);
    driver->mac[4] = temp &0xff;
    driver->mac[5] = temp >> 8;
    return 1;
}
void enableInterrupt()
{
    writeCommand(REG_IMASK ,0x1F6DC);
    writeCommand(REG_IMASK ,0xff & ~4);
    readCommand(0xc0);
}

void txinit(struct NetworkDriver *driver,struct MEMMAN *memman)
{    
    unsigned char *  ptr;
    struct e1000_tx_desc *descs;
    // Allocate buffer for receive descriptors. For simplicity, in my case khmalloc returns a virtual address that is identical to it physical mapped address.
    // In your case you should handle virtual and physical addresses as the addresses passed to the NIC should be physical ones
    //ptr = (uint8_t *)(kmalloc_ptr->khmalloc(sizeof(struct e1000_tx_desc)*E1000_NUM_TX_DESC + 16));
    ptr = (unsigned char * )memman_alloc_4k(memman, sizeof(struct e1000_tx_desc)*E1000_NUM_TX_DESC+16 );
    descs = (struct e1000_tx_desc *)ptr;
    int i;
    for(i = 0; i < E1000_NUM_TX_DESC; i++)
    {
        driver->tx_descs[i] = (struct e1000_tx_desc *)((unsigned char*)descs + i*16);
        *((unsigned int *)(driver->tx_descs[i]->addr)) = 0;
        driver->tx_descs[i]->cmd = 0;
        driver->tx_descs[i]->status = TSTA_DD;// Descriptor Done (1<<0)
    }
    //#define REG_TXDESCLO    0x3800, high bit register of tx description
    //#define REG_TXDESCHI    0x3804
    //writeCommand(REG_TXDESCHI, (uint32_t)(    (uint64_t)ptr >> 32) );
    //writeCommand(REG_TXDESCLO, (uint32_t)((uint64_t)ptr & 0xFFFFFFFF));
 
    writeCommand(REG_TXDESCHI, (unsigned int)(ptr+4) ); // high bits
    writeCommand(REG_TXDESCLO, (unsigned int)ptr );   // low bits
 
    //now setup total length of descriptors /tx desc len
    writeCommand(REG_TXDESCLEN, E1000_NUM_TX_DESC * 16);
 
 
    //setup numbers  tx desc head,tail
    writeCommand( REG_TXDESCHEAD, 0);
    writeCommand( REG_TXDESCTAIL, 0);
    driver->tx_cur = 0;
    writeCommand(REG_TCTRL,  TCTL_EN
        | TCTL_PSP
        | (15 << TCTL_CT_SHIFT)
        | (64 << TCTL_COLD_SHIFT)
        | TCTL_RTLC);
 
    // This line of code overrides the one before it but I left both to highlight that the previous one works with e1000 cards, but for the e1000e cards 
    // you should set the TCTRL register as follows. For detailed description of each bit, please refer to the Intel Manual.
    // In the case of I217 and 82577LM packets will not be sent if the TCTRL is not configured using the following bits.
    //writeCommand(REG_TCTRL,  0b0110000000000111111000011111010);
    //writeCommand(REG_TCTRL,  0b011 0000 0000 0011 1111 0000 1111 1010);
    writeCommand(REG_TCTRL, 0x3003f0fa);
    writeCommand(REG_TIPG,  0x0060200A);
 
}

  
void rxinit(struct NetworkDriver *driver,struct MEMMAN *memman)
{    
    unsigned  char * ptr;
    struct e1000_rx_desc *descs;
 
    // Allocate buffer for receive descriptors. For simplicity, in my case khmalloc returns a virtual address that is identical to it physical mapped address.
    // In your case you should handle virtual and physical addresses as the addresses passed to the NIC should be physical ones
    ptr = (unsigned char * )memman_alloc_4k(memman, sizeof(struct e1000_tx_desc)*E1000_NUM_TX_DESC+16 );
    
    //ptr = (uint8_t *)(kmalloc_ptr->khmalloc(sizeof(struct e1000_rx_desc)*E1000_NUM_RX_DESC + 16));
 
    descs = (struct e1000_rx_desc *)ptr;
    int i;
    for(i = 0; i < E1000_NUM_RX_DESC; i++)
    {
        driver->rx_descs[i] = (struct e1000_rx_desc *)((unsigned char *)descs + i*16);
        //driver->rx_descs[i]->addr = (uint64_t)(uint8_t *)(kmalloc_ptr->khmalloc(8192 + 16));
        *((unsigned int *)(driver->tx_descs[i]->addr)) =(unsigned int*)memman_alloc_4k(memman,8192+16);
        driver->rx_descs[i]->status = 0;
    }
 
    //writeCommand(REG_TXDESCLO, (uint32_t)((uint64_t)ptr >> 32) );
    //writeCommand(REG_TXDESCHI, (uint32_t)((uint64_t)ptr & 0xFFFFFFFF));
 
    writeCommand(REG_TXDESCLO, (unsigned int)(ptr+4) );
    writeCommand(REG_TXDESCHI, (unsigned int)ptr);
 
    //writeCommand(REG_RXDESCLO, (uint64_t)ptr);
    writeCommand(REG_RXDESCLO, (unsigned int)ptr);
    writeCommand(REG_RXDESCHI, 0);
 
    writeCommand(REG_RXDESCLEN, E1000_NUM_RX_DESC * 16);
 
    writeCommand(REG_RXDESCHEAD, 0);
    writeCommand(REG_RXDESCTAIL, E1000_NUM_RX_DESC-1);
    driver->rx_cur = 0;
    writeCommand(REG_RCTRL, RCTL_EN| RCTL_SBP| RCTL_UPE | RCTL_MPE | RCTL_LBM_NONE | RTCL_RDMTS_HALF | RCTL_BAM | RCTL_SECRC  | RCTL_BSIZE_8192);
}

// start work
//The start method basically detects the EEPROM, reads the MAC addresses, setup rx and tx buffers, register the interrupt handler, and enable NIC interrupts
/*
void start(){
    detectEEProm();
    //readMACAddress();
    // what is startLink?
    //startLink();
 
    for(int i = 0; i < 0x80; i++)
        writeCommand(0x5200 + i*4, 0);

    // 完成中断函数配置：if ( interruptManager->registerInterrupt(IRQ0+pciConfigHeader->getIntLine(),this))
    
    enableInterrupt();
    rxinit();
    txinit();        
    //    video.putString("E1000 card started\n",COLOR_RED,COLOR_WHITE);
    
}
*/